package jp.tkym.labs.store;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import jp.tkym.labs.store.api.BeanQuery;
import jp.tkym.labs.store.api.BeanQueryFilter;
import jp.tkym.labs.store.api.BeanQueryFilterOperator;
import jp.tkym.labs.store.api.BeanQuerySortDirection;
import jp.tkym.labs.store.api.BeanQuerySorter;
import jp.tkym.labs.store.api.BeanstoreException;
import jp.tkym.labs.store.api.EntityConfigProvider;
import jp.tkym.labs.store.api.EntityConfigProvider.EntityConfig;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;

/**
 * BeanQuery for DatastoreService
 * 
 * @author kazunari
 * @param <B>
 */
class GoogleBeanQuery<B> implements BeanQuery<B> {
	private DatastoreService ds;
	private EntityResolver<B> resolver;
	private List<BeanQueryFilter> filterList = new ArrayList<BeanQueryFilter>();
	private List<BeanQuerySorter> sorterList = new ArrayList<BeanQuerySorter>();
	private EntityConfig entityConfig;

	GoogleBeanQuery(DatastoreService ds, Class<B> cls) {
		this(ds, cls, null);
	}

	GoogleBeanQuery(DatastoreService ds, Class<B> cls, Key parent) {
		entityConfig = EntityConfigProvider.getInstance().get(cls);
		resolver = new EntityResolver<B>(cls, parent,
				entityConfig.getKeyProperty());
		this.ds = ds;
	}

	@Override
	public BeanQuery<B> filter(BeanQueryFilter... filters) {
		for (BeanQueryFilter e : filters)
			filterList.add(e);
		return this;
	}

	@Override
	public BeanQuery<B> sort(BeanQuerySorter... sorters) {
		for (BeanQuerySorter e : sorters)
			sorterList.add(e);
		return this;
	}

	/**
	 * make PreparedQuery adding Filter and Sorter
	 * 
	 * @return
	 */
	Query makeQuery() {
		Query query = resolver.createQuery();
		for (BeanQueryFilter e : filterList)
			addFilterToQuery(query, e);
		for (BeanQuerySorter e : sorterList)
			addSorterToQuery(query, e);
		return query;
	}

	void addSorterToQuery(Query query, BeanQuerySorter sorter) {
		String propertyName = sorter.getPropertyName();
		BeanQuerySortDirection direction = sorter.getSortOperation();
		if (direction.equals(BeanQuerySortDirection.ASCENDING)) {
			query.addSort(propertyName, SortDirection.ASCENDING);
		} else if (direction.equals(BeanQuerySortDirection.DESCENDING)) {
			query.addSort(propertyName, SortDirection.DESCENDING);
		}
	}

	void addFilterToQuery(Query query, BeanQueryFilter filter) {
		String propertyName = filter.getPropertyName();
		BeanQueryFilterOperator operator = filter.getFilterOperator();
		Object value = filter.getValue();
		if (operator.equals(BeanQueryFilterOperator.EQUAL)) {
			query.addFilter(propertyName, FilterOperator.EQUAL, value);
		} else if (operator.equals(BeanQueryFilterOperator.NOT_EQUAL)) {
			query.addFilter(propertyName, FilterOperator.NOT_EQUAL, value);
		} else if (operator.equals(BeanQueryFilterOperator.GREATER_THAN)) {
			query.addFilter(propertyName, FilterOperator.GREATER_THAN,
					value);
		} else if (operator
				.equals(BeanQueryFilterOperator.GREATER_THAN_OR_EQUAL)) {
			query.addFilter(propertyName,
					FilterOperator.GREATER_THAN_OR_EQUAL, value);
		} else if (operator.equals(BeanQueryFilterOperator.LESS_THAN)) {
			query.addFilter(propertyName, FilterOperator.LESS_THAN, value);
		} else if (operator
				.equals(BeanQueryFilterOperator.LESS_THAN_OR_EQUAL)) {
			query.addFilter(propertyName,
					FilterOperator.LESS_THAN_OR_EQUAL, value);
		} else {
			throw new UnsupportedOperationException("unsupport type = "
					+ operator.toString());
		}
	}

	FetchOptions createFetchOptions() {
		FetchOptions options = FetchOptions.Builder.withDefaults();
		return options;
	}

	@Override
	public List<B> asList() throws BeanstoreException {
		return asList(createFetchOptions());
	}

	List<B> asList(FetchOptions options) {
		PreparedQuery preparedQuery = ds.prepare(makeQuery());
		List<Entity> entityList = preparedQuery.asList(options);
		List<B> beanList = new ArrayList<B>();
		for (Entity entity : entityList) {
			System.out.println(entity.getKey());
			beanList.add(resolver.toBean(entity));
		}
		return beanList;
	}

	@Override
	public Iterator<B> asIterator() throws BeanstoreException {
		PreparedQuery preparedQuery = ds.prepare(makeQuery());
		Iterator<Entity> entityIterator = preparedQuery
				.asIterator(createFetchOptions());
		return new GoogleQueryIterator<B>(resolver, entityIterator);
	}

	@Override
	public Set<Object> keySet() throws BeanstoreException {
		throw new UnsupportedOperationException();
	}
}